﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Athena.Core;
using Athena.Objects;

namespace Athena.IO
{
    public class XMLReader
    {
        public static Model ReadModel(System.IO.Stream oStream)
        {
            XmlTextReader xmlReader = new XmlTextReader(oStream);

            xmlReader.Read(); //document
            xmlReader.Read(); //model

            Core.Item item = IO.XMLReader.ReadItem(xmlReader, null);
            Module module = item as Module;

            if (module == null) return null;

            Model model = new Model();

            if (module.Drawable != null)
            {
                model.AddToModel(module.Drawable, false, false, false, false);
            }
            else
            {
                model.BackEndModel = module;
                foreach (Core.Item i in module.LocalItems)
                {
                    if (i.Drawable == null) i.GenerateDrawable(new System.Drawing.PointF());
                    i.Drawable.Parent = null;
                    if (i.Drawable != null)
                        model.AddToModel(i.Drawable, false, false, false, false);
                }
            }

            if (Model.Empty != null)
            {
                foreach (Objects.IDrawable id in Model.Empty.Alias)
                {
                    IGlyph ig = id as IGlyph;
                    if (ig != null)
                    {
                        List<Connector> incoming = ig.IncomingConnections,
                                        outgoing = ig.OutgoingConnections;
                        foreach (Connector c in incoming)
                            if (model.Drawables.Contains(c))
                                model.Drawables.Add(ig);
                        foreach (Connector c in outgoing)
                            if (model.Drawables.Contains(c))
                                model.Drawables.Add(ig);
                    }
                }
            }

            model.BackEndModel.Drawable = new ModuleGlyph();

            if (xmlReader.Name != "NullItems")
                xmlReader.Read();

            if (xmlReader.Name == "NullItems")
                ReadNullItems(xmlReader, model);

            model.BackEndModel.Drawable = null;

            xmlReader.Close();

            return model;
        }

        public static Item ReadItem(XmlReader reader, Item parent)
        {
            bool read = reader.Read();
            if (!read || reader.NodeType != XmlNodeType.Element) return null;

            Item item = null;
            string name = reader.Name;

            switch (name)
            {
                case "Edge":
                    item = new Edge();
                    break;
                case "Promoter":
                    item = new Promoter();
                    break;
                case "Gene":
                    item = new Gene();
                    break;
                case "Terminator":
                    item = new Terminator();
                    break;
                case "Part":
                    item = new Part();
                    break;
                case "GlobalSpecies":
                    item = new GlobalSpecies();
                    break;
                case "Species":
                    item = new Species();
                    break;
                case "Compartment":
                    item = new Compartment();
                    break;
                case "Module":
                    item = new Module();
                    break;
                default:
                    return null;
            }
            if (item != null)
            {
                item.Parent = parent;
                item.ReadXML(reader);

                //while (reader.NodeType != XmlNodeType.EndElement || reader.Name != name)
                //    if (!reader.Read()) break;

                if (item.ID == Module.Empty.ID) 
                    item = Module.Empty;
                if (!(reader.NodeType == XmlNodeType.Element))// && reader.Name == "Drawable"))
                    reader.Read();

                if (reader.NodeType == XmlNodeType.Element && reader.Name == "Drawable")
                {
                    if (parent != null)
                        ReadDrawable(reader, item, parent.Drawable);
                    else
                        ReadDrawable(reader, item, null);

                    reader.Read();
                }
                else
                {
                    if (item.ParentModule == null) //main module
                    {
                        if (item.Drawable is ContainerGlyph)
                        {
                            ((ContainerGlyph)item.Drawable).GenerateDrawablesFromItem();
                            //((ContainerGlyph)item.Drawable).AddDrawablesToModel(Program.Model);
                        }
                    }
                    item.Drawable = null;
                }
            }
            return item;
        }

        public static IDrawable ReadDrawable(XmlReader reader, Item core, IDrawable parent)
        {
            bool read = reader.Read();
            if (!read || reader.NodeType != XmlNodeType.Element) return null;

            IDrawable item = null;
            string name = reader.Name;

            switch (name)
            {
                case "ModuleConnector":
                    item = new ModuleConnector();
                    break;
                case "BezierConnector":
                    item = new BezierConnector();
                    break;
                case "PromoterGlyph":
                    item = new PromoterGlyph();
                    break;
                case "GeneGlyph": 
                    item = new GeneGlyph();
                    break;
                case "TerminatorGlyph":
                    item = new TerminatorGlyph();
                    break;
                case "RBSGlyph":
                    item = new RbsGlyph();
                    break;
                case "PartGlyph":
                    item = new PartGlyph();
                    break;
                case "SpeciesGlyph":
                    item = new SpeciesGlyph();
                    break;
                case "MultimericSpecies":
                    item = new MultimericSpecies();
                    break;
                case "CompartmentGlyph":
                    new CompartmentGlyph();
                    break;
                case "ModuleGlyph":
                    item = new ModuleGlyph();
                    break;
                case "EmptyGlyph":
                    item = new EmptyGlyph();
                    item.Remove();
                    break;
                case "Decorator":
                    item = new Decorator();
                    break;
                case "Text":
                    item = new TextGlyph();
                    break;
                default:
                    return null;
            }
            if (item != null)
            {
                if (core is GlobalSpecies)
                {
                    item = core.Drawable;
                }
                else
                {
                    item.Item = core;
                    item.Parent = parent;
                    if (parent == null)
                        Program.Model.Drawables.Add(item);
                }
                item.ReadXML(reader);

                //while (reader.NodeType != XmlNodeType.EndElement || reader.Name != name)
                 //   if (!reader.Read()) break;

                reader.Read();
                
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "Aliases")
                {
                    while (read)
                    {
                        IDrawable id = IO.XMLReader.ReadDrawable(reader, null, parent);
                        if (id == null)
                            break;
                        else
                            item.AddAlias(id);
                    }
                    reader.Read();
                }

            }
            return item;
        }

        public static void ReadNullItems(XmlReader reader, Model currentModel)
        {
            bool read = true;
            while (read)
            {
                IDrawable item = IO.XMLReader.ReadDrawable(reader, null, currentModel.BackEndModel.Drawable);
                if (item == null)
                    break;
                else
                    currentModel.Drawables.Add(item);
                //read = reader.Read();
            }
        }
    }
}
